21  Two port parameters

21.1 Introduction

This notebook describes the calculation of two port parameters derived from the circuit’s netlist. A Two-port network is a circuit that has two pairs of ports or terminals and the circuit is characterized by the currents and voltages at the ports by a 2 by 2 matrix. From a two port matrix the response of the network to signals applied to the ports can be calculated easily, without solving for all the internal voltages and currents in the network. For example filters, matching networks, transmission lines, and transistors are characterized by two port parameters.

21.2 Circuit description

The first circuit is an attenuator, which could be easily analyzed by hand with a pencil, paper and a calculator, but here I’m using python to step through the calculations. These same steps can be applied to larger, more complex circuits which would be difficult to analyze by hand. The Sympy and SciPy libraries can be used to obtain symbolic and numerical results, however as is shown below, when the circuit is large, symbolic results are not that useful nor do they provide much insight.

The procedure starts by first drawing a schematic of the circuit with a schematic capture program such as EasyEDA (links to the schematics are provided below) then the netlist is generated and exported as a text file. The netlist is used to generate modified nodal equations with the python program described here. The equations are solved for voltages and currents at the port terminals by using SymPy to generate the z-paramters. Z-parameters are also known as open-circuit impedance parameters as they are calculated under open circuit conditions. Once the z-parameters are obtained, these can be converted into other parameters, such as Y, H, S or others.

The second circuit is a band pass filter. The filter is designed from a low pass prototype by using normalized values from a filter design handbook. Z and s-parameters are calculated and the results are plotted using Matplotlib.

from sympy import *
import numpy as np
from tabulate import tabulate
from scipy import signal
import matplotlib.pyplot as plt
import pandas as pd
import SymMNA
from IPython.display import display, Markdown, Math, Latex
init_printing()

21.2.1 Pi-attenuator

The pi-attenuator, shown in Figure 21.1, is formed by three resistors with R1 and R3 as the shunt resistors on the input and output ports and R2 as the series resistor in a circuit topology in the shape of the greek letter pi. For R1 = R3 = 96.25 ohms and R2 equal to 71.15 ohms, the attenuation is 10 dB and the port impedance is 50 ohms. Pasternack’s Pi Attenuator Calculator (Pi pad attenuator) was used to design the attenuator.

The selection of which type a network parameter is somewhat a matter of convenience. However, some circuits can’t have their ports shorted or left open for proper operation, so s-parameters are the appropriate choice in this situation. Solving for the z-parameters is convenient since all we need to do is apply a voltage source to the input port, with the output port unterminated, and solve for the voltage and currents at each port. Since the output port is unterminated, the current into this port is zero and we can find z11 and z21. To find z21 and z22, the voltage source is applied to port 2 with port 1 unterminated.

To find Z-parameters attach voltage sources to both ports and find the node equations. LTSpice was used to draw the schematic.

Figure 21.1: Pi attenuator schematic

The netlist for the circuit:

R1 0 1 96.25
R2 1 2 71.15
R3 0 2 96.25
V1 1 0 1
V2 2 0 1

21.2.1.1 Find z11 & z21

To find \(z_{11}\) and \(z_{21}\), remove V2 from the net list by commenting out the V2 line in the net list. This will make \(I_2\) equal to zero. \(z_{11}\) is equal to \(\frac{V_1}{I_1}\text{ when }I_2=0\) and \(z_{21}\) is equal to \(\frac{V_2}{I_1}\text{ when }I_2=0\).

The net list below was run through the NMA code to generate the circuit equations.

R1 0 1 96.25
R2 1 2 71.15
R3 0 2 96.25
V1 1 0 1
*V2 2 0 1
net_list = '''
R1 0 1 96.25
R2 1 2 71.15
R3 0 2 96.25
V1 1 0 1
*V2 2 0 1
'''

Call the symbolic modified nodal analysis function

report, network_df, i_unk_df, A, X, Z = SymMNA.smna(net_list)

The network equations for the circuit can be obtained from the A, X and Z values returned from the smna function. The A, X and Z are formuloated into equations and displayed below. Markdown is an IPython function and latex is a SymPy printing function.

# reform X and Z into Matrix type for printing
Xp = Matrix(X)
Zp = Matrix(Z)
temp = ''
for i in range(len(X)):
    temp += '${:s}$<br>'.format(latex(Eq((A*Xp)[i:i+1][0],Zp[i])))

Markdown(temp)

\(I_{V1} + v_{1} \cdot \left(\frac{1}{R_{2}} + \frac{1}{R_{1}}\right) - \frac{v_{2}}{R_{2}} = 0\)
\(v_{2} \cdot \left(\frac{1}{R_{3}} + \frac{1}{R_{2}}\right) - \frac{v_{1}}{R_{2}} = 0\)
\(v_{1} = V_{1}\)

# Put matrices into SymPy 
X = Matrix(X)
Z = Matrix(Z)

NE_sym = Eq(A*X,Z)
NE_sym

\(\displaystyle \left[\begin{matrix}I_{V1} + v_{1} \cdot \left(\frac{1}{R_{2}} + \frac{1}{R_{1}}\right) - \frac{v_{2}}{R_{2}}\\v_{2} \cdot \left(\frac{1}{R_{3}} + \frac{1}{R_{2}}\right) - \frac{v_{1}}{R_{2}}\\v_{1}\end{matrix}\right] = \left[\begin{matrix}0\\0\\V_{1}\end{matrix}\right]\)

# turn the free symbols into SymPy variables
var(str(NE_sym.free_symbols).replace('{','').replace('}',''))

\(\displaystyle \left( R_{3}, \ V_{1}, \ v_{2}, \ v_{1}, \ R_{1}, \ I_{V1}, \ R_{2}\right)\)

Symbolic solution

U_sym_p1 = solve(NE_sym,X)

Display the symbolic solution

temp = ''
for i in U_sym_p1.keys():
    temp += '${:s} = {:s}$<br>'.format(latex(i),latex(U_sym_p1[i]))

Markdown(temp)

\(v_{1} = V_{1}\)
\(v_{2} = \frac{R_{3} V_{1}}{R_{2} + R_{3}}\)
\(I_{V1} = \frac{- R_{1} V_{1} - R_{2} V_{1} - R_{3} V_{1}}{R_{1} R_{2} + R_{1} R_{3}}\)

\(z_{11} = \frac {V_1}{I_1} \text{ when } I_2=0\)

A negative sign was placed in the equation to make the direction of the current flow match the definition of the two port parameters.

z11 = -U_sym_p1[v1]/U_sym_p1[I_V1]
z11.cancel()

\(\displaystyle \frac{R_{1} R_{2} + R_{1} R_{3}}{R_{1} + R_{2} + R_{3}}\)

\(z_{21} = \frac {V_2}{I_1} \text{ when } I_2=0\)

z21 = -U_sym_p1[v2]/U_sym_p1[I_V1]
z21.cancel()

\(\displaystyle \frac{R_{1} R_{3}}{R_{1} + R_{2} + R_{3}}\)

21.2.1.2 Find z12 & z22

To find \(z_{12}\) and \(z_{22}\), remove V1 from the net list by commenting out the V1 line in the netlist. This will make \(I_1\) equal to zero. \(z_{12}\) is equal to \(\frac{V_1}{I_2}\text{ when }I_1=0\) and \(z_{22}\) is equal to \(\frac{V_2}{I_2}\text{ when }I_1=0\).

The net list below was run through the NMA code to generate the circuit equations.

Remove V1 from the net list.

R1 0 1 96.25
R2 1 2 71.15
R3 0 2 96.25
*V1 1 0 1
V2 2 0 1
net_list = '''
R1 0 1 96.25
R2 1 2 71.15
R3 0 2 96.25
*V1 1 0 1
V2 2 0 1
'''

Call the symbolic modified nodal analysis function

report, network_df, i_unk_df, A, X, Z = SymMNA.smna(net_list)

The network equations for the circuit can be obtained from the A, X and Z values returned from the smna function. The A, X and Z are formuloated into equations and displayed below. Markdown is an IPython function and latex is a SymPy printing function.

# reform X and Z into Matrix type for printing
Xp = Matrix(X)
Zp = Matrix(Z)
temp = ''
for i in range(len(X)):
    temp += '${:s}$<br>'.format(latex(Eq((A*Xp)[i:i+1][0],Zp[i])))

Markdown(temp)

\(v_{1} \cdot \left(\frac{1}{R_{2}} + \frac{1}{R_{1}}\right) - \frac{v_{2}}{R_{2}} = 0\)
\(I_{V2} + v_{2} \cdot \left(\frac{1}{R_{3}} + \frac{1}{R_{2}}\right) - \frac{v_{1}}{R_{2}} = 0\)
\(v_{2} = V_{2}\)

# Put matrices into SymPy 
X = Matrix(X)
Z = Matrix(Z)

NE_sym = Eq(A*X,Z)
NE_sym

\(\displaystyle \left[\begin{matrix}v_{1} \cdot \left(\frac{1}{R_{2}} + \frac{1}{R_{1}}\right) - \frac{v_{2}}{R_{2}}\\I_{V2} + v_{2} \cdot \left(\frac{1}{R_{3}} + \frac{1}{R_{2}}\right) - \frac{v_{1}}{R_{2}}\\v_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\V_{2}\end{matrix}\right]\)

# turn the free symbols into SymPy variables
var(str(NE_sym.free_symbols).replace('{','').replace('}',''))

\(\displaystyle \left( R_{3}, \ I_{V2}, \ V_{2}, \ v_{2}, \ v_{1}, \ R_{1}, \ R_{2}\right)\)

Symbolic solution

U_sym_p2 = solve(NE_sym,X)

Display the symbolic solution

temp = ''
for i in U_sym_p2.keys():
    temp += '${:s} = {:s}$<br>'.format(latex(i),latex(U_sym_p2[i]))

Markdown(temp)

\(v_{1} = \frac{R_{1} V_{2}}{R_{1} + R_{2}}\)
\(v_{2} = V_{2}\)
\(I_{V2} = \frac{- R_{1} V_{2} - R_{2} V_{2} - R_{3} V_{2}}{R_{1} R_{3} + R_{2} R_{3}}\)

\(z_{12}=\frac{V_1}{I_2}\text{ when }I_1=0\)

z12 = -U_sym_p2[v1]/U_sym_p2[I_V2]
z12.cancel()

\(\displaystyle \frac{R_{1} R_{3}}{R_{1} + R_{2} + R_{3}}\)

\(z_{22}=\frac{V_2}{I_2}\text{ when }I_1=0\)

z22 = -U_sym_p2[v2]/U_sym_p2[I_V2]
z22.cancel()

\(\displaystyle \frac{R_{1} R_{3} + R_{2} R_{3}}{R_{1} + R_{2} + R_{3}}\)

The common names for the z-parameters are:
z11: input driving point impedance
z12: reverse transfer impedance
z21: forward transfer impedance
z22: output driving point impedance

The elements of the matrix can be displayed:

Matrix([[z11,z21],[z12,z22]])

\(\displaystyle \left[\begin{matrix}- \frac{V_{1} \left(R_{1} R_{2} + R_{1} R_{3}\right)}{- R_{1} V_{1} - R_{2} V_{1} - R_{3} V_{1}} & - \frac{R_{3} V_{1} \left(R_{1} R_{2} + R_{1} R_{3}\right)}{\left(R_{2} + R_{3}\right) \left(- R_{1} V_{1} - R_{2} V_{1} - R_{3} V_{1}\right)}\\- \frac{R_{1} V_{2} \left(R_{1} R_{3} + R_{2} R_{3}\right)}{\left(R_{1} + R_{2}\right) \left(- R_{1} V_{2} - R_{2} V_{2} - R_{3} V_{2}\right)} & - \frac{V_{2} \left(R_{1} R_{3} + R_{2} R_{3}\right)}{- R_{1} V_{2} - R_{2} V_{2} - R_{3} V_{2}}\end{matrix}\right]\)

Actual values for each of the components can be substituted for the symbols with R1 = R3 = 96.25 ohms and R2 equal to 71.15 ohms

atten_values = {R1:96.25,R2:71.15,R3:96.25}
z11_val = z11.subs(atten_values)
z12_val = z12.subs(atten_values)
z21_val = z21.subs(atten_values)
z22_val = z22.subs(atten_values)

The numeric values of the impedance matrix

np.array([[z11_val, z12_val],[z21_val, z22_val]],dtype=float)
array([[61.11227005, 35.13772995],
       [35.13772995, 61.11227005]])

21.2.1.3 Calculate Z in and Z out, ki and kv

A two-port circuit is typically driven at port 1 and loaded at port 2, and four equations needed to solve the four unknowns. The link here has a nice explnation of two port analysis.

\(\begin{align*} \begin{bmatrix} -1 & 0 & z11 & z12 \\\ 0 & -1 & z21 & z22 \\\ 1 & 0 & Z_S & 0 \\\ 0 & 1 & 0 & Z_L \end{bmatrix} \begin{bmatrix} V_1 \\ V_2 \\ I_1 \\ I_2 \end{bmatrix} {} &= \begin{bmatrix} 0 \\ 0 \\ V_g \\ 0 \end{bmatrix} \end{align*}\)

Putting into SymPy

Z11, Z12, Z21, Z22 = symbols('Z11 Z12 Z21 Z22')
Z_s, Z_l, Vg = symbols('Z_s Z_l Vg')

eqZ = Eq(Matrix([[-1,0,Z11,Z12],[0,-1,Z21,Z22],[1,0,Z_s,0],[0,1,0,Z_l]])*Matrix([V1,V2,I_V1,I_V2]),Matrix([0,0,Vg,0]))
eqZ

\(\displaystyle \left[\begin{matrix}I_{V1} Z_{11} + I_{V2} Z_{12} - V_{1}\\I_{V1} Z_{21} + I_{V2} Z_{22} - V_{2}\\I_{V1} Z_{s} + V_{1}\\I_{V2} Z_{l} + V_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\Vg\\0\end{matrix}\right]\)

ansZ = solve(eqZ,[V1, V2, I_V1,I_V2])
ansZ

\(\displaystyle \left\{ I_{V1} : \frac{Vg Z_{22} + Vg Z_{l}}{Z_{11} Z_{22} + Z_{11} Z_{l} - Z_{12} Z_{21} + Z_{22} Z_{s} + Z_{l} Z_{s}}, \ I_{V2} : - \frac{Vg Z_{21}}{Z_{11} Z_{22} + Z_{11} Z_{l} - Z_{12} Z_{21} + Z_{22} Z_{s} + Z_{l} Z_{s}}, \ V_{1} : \frac{Vg Z_{11} Z_{22} + Vg Z_{11} Z_{l} - Vg Z_{12} Z_{21}}{Z_{11} Z_{22} + Z_{11} Z_{l} - Z_{12} Z_{21} + Z_{22} Z_{s} + Z_{l} Z_{s}}, \ V_{2} : \frac{Vg Z_{21} Z_{l}}{Z_{11} Z_{22} + Z_{11} Z_{l} - Z_{12} Z_{21} + Z_{22} Z_{s} + Z_{l} Z_{s}}\right\}\)

# current gain
ansZ[I_V2]/ansZ[I_V1]

\(\displaystyle - \frac{Vg Z_{21}}{Vg Z_{22} + Vg Z_{l}}\)

# voltage gain
ansZ[V2]/ansZ[V1]

\(\displaystyle \frac{Vg Z_{21} Z_{l}}{Vg Z_{11} Z_{22} + Vg Z_{11} Z_{l} - Vg Z_{12} Z_{21}}\)

# Z input
ansZ[V1]/ansZ[I_V1]

\(\displaystyle \frac{Vg Z_{11} Z_{22} + Vg Z_{11} Z_{l} - Vg Z_{12} Z_{21}}{Vg Z_{22} + Vg Z_{l}}\)

# Z out
ansZ[V2]/ansZ[I_V2]

\(\displaystyle - Z_{l}\)

Sympy returns the output impedance as simply \(-Z_l\), since this is the algebraic simplification of the equations. Most textbooks will give the output impedance in terms of the z-parameters and the source impedance: \(Z_{out}=Z_{22}-\frac{Z_{12}Z_{21}}{Z_{11}+Z_{S}}\)

21.2.1.4 Calculate input and output impedance for the attenuator

The input impedance of a two-port network is: \(Z_{in}=\frac{Z_{11}Z_{22}+Z_{11}Z_{L}-Z_{12}Z_{21}}{Z_{22}+Z_{L}}\)
Textbook equation: \(Z_{in}=Z_{11}-\frac{Z_{12}Z_{21}}{Z_{22}+Z_{L}}\) where \(Z_{L}\) is the impedance of the load connected to port two.

The output impedance from textbooks is: \(Z_{out}=Z_{22}-\frac{Z_{12}Z_{21}}{Z_{11}+Z_{S}}\), where \(Z_{S}\) is the impedance of the source connected to port one.

# Zin
Zl = 50 # load impedance
print('input impedance of network from z-parameters: {:.2f}'.format(np.abs((z11_val*z22_val+z11_val*Zl-z12_val*z21_val)/(z22_val+Zl))))
input impedance of network from z-parameters: 50.00
# Zout
Zs = 50 # source impedance
print('output impedance of network from z-parameters: {:.2f}'.format(np.abs(z22_val-(z12_val*z21_val)/(z11_val+Zs))))
output impedance of network from z-parameters: 50.00

21.2.1.5 Calculate the voltage gain for the attenuator

Voltage gain: \(K_v=\frac{Z_{21}Z_{L}}{Z_{11}Z_{22}+Z_{11}Z_{L}-Z_{12}Z_{21}}\), from text books: \(K_v=\frac{Z_{21}Z_{L}}{\Delta _Z+Z_{11}Z_L}\)

#Kv
print('voltage gain of network from z-parameters: {:.2f} dB'.format(20*np.log10(float((z21_val*Zl)/(z11_val*z22_val + z11_val*Zl - z12_val*z21_val)))))
voltage gain of network from z-parameters: -10.00 dB

21.2.1.6 Convert z-parameters to y-parameters

The z-parameters can be converted to admittance or y-parameters with the following code:

Find the determinant of the z-parameter matrix:

det_z = np.linalg.det(np.array([[z11_val, z12_val],[z21_val, z22_val]],dtype=float))
print('determinant = {:.2f}'.format(det_z))
determinant = 2500.05

Display the y-parameter matrix.

np.array([[z22_val/det_z,-z12_val/det_z],[-z21_val/det_z,z11_val/det_z]],dtype=float)
array([[ 0.02444442, -0.01405481],
       [-0.01405481,  0.02444442]])

21.2.1.7 Convert z-parameters to s-parameters

The 2-port S-parameters have the following generic descriptions:
\(S_{11} \text{ is the input port voltage reflection coefficient}\)
\(S_{12} \text{ is the reverse voltage gain}\)
\(S_{21} \text{ is the forward voltage gain}\)
\(S_{22} \text{ is the output port voltage reflection coefficient}\)

The z-parameter can be converted to scattering or s-parameters with the following:
\(s_{11} = \frac {(Z_{11}-Z_o)(Z_{22}+Z_o)-Z_{12}Z_{21}} {\Delta Z}\)
\(s_{12} = \frac {2Z_{12}Z_o} {\Delta Z}\)
\(s_{21} = \frac {2Z_{21}Z_o} {\Delta Z}\)
\(s_{22} = \frac {(Z_{11}+Z_o)(Z_{22}-Z_o)-Z_{12}Z_{21}} {\Delta Z}\)
where: \(\Delta Z = (Z_{11}+Z_o)(Z_{22}+Z_o)-Z_{12}Z_{21}\) and \(Z_o\) is the characteristic impedance.

Z_o = 50 # characteristic impedance
del_z = (z11_val+Z_o)*(z22_val+Z_o)-z12_val*z21_val

S = np.array([[((z11_val-Z_o)*(z22_val-Z_o)-z12_val*z21_val)/del_z,(2*z12_val*Z_o)/del_z],
              [(2*z21_val*Z_o)/del_z,((z11_val+Z_o)*(z22_val-Z_o)-z12_val*z21_val)/del_z]],dtype=float)
S # display the s-parameter matrix
array([[-1.00004488e-01,  3.16234863e-01],
       [ 3.16234863e-01,  4.45363654e-06]])

21.2.1.8 Input return loss

Input return loss \(RL_{in}\) indicates impedance match of the port to the source. A number of 10 dB or greater, indicates that the match is probably acceptable.

\(RL_{in} =-20\log_{10}\left|S_{11}\right|\)

print('Input return loss = {:.2f} dB'.format(-20*np.log10(np.abs(S[0,0]))))
Input return loss = 20.00 dB

21.2.1.9 Insertion loss

Insertion loss \(IL\) is the reciprocal of the magnitude of the transmission coefficient, S21, expressed in decibels.

\(IL=-20\log _{10} \left|S_{21} \right|\)

print('Insertion loss = {:.2f} dB'.format(-20*np.log10(np.abs(S[1,0]))))
Insertion loss = 10.00 dB

This value agrees with the design of the attenuator as being a 10 dB attenuator.

21.3 Band Pass Filter

The circuit in Figure 21.2 is a band pass filter designed from normalized filter design tables. The third order low pass filter prototype was transformed into a bandpass filter as shown in the figure below. Butterworth filter coefficients were chosen for this design. A Butterworth filter is a type of signal processing filter that has a flat passband response.

Figure 21.2: Band pass filter schematic

The low pass to bandpass transformation calculations are shown below. The filter was designed to have a center frequency of 10 MHz and a 3 dB bandwidth of 1 MHz. The normalized filter values were obtained from Williams and Taylor (1995):

3rd order Butterworth LPF prototype
Rs/Rl = 1
C1 = 1
L2 = 2
C1 = 1

The bandpass filter has the following design parameters: The source and load impedance is 50 ohms, the filter bandwidth is 1 MHz and the center frequency is 10 MHz. Following the example from Williams and Taylor (1995) (example 5-2), but with the following changes:

  • center freq = 10MHz
  • bandwidth 1MHz
  • Rs=Rl=50
f_center = 10e6
f_3dB_BW = 1e6 # 3dB bandwidth
fo = np.sqrt((f_center-f_3dB_BW/2)*(f_center+f_3dB_BW/2)) # geometric center frequency
print('geometric center frequency = {:.3f}MHz'.format(fo/1e6))
geometric center frequency = 9.987MHz
Z = 50 # load and source resistance
FSF = 2*np.pi*f_3dB_BW
print('frequency scaling factor = {:.3f}'.format(FSF))
frequency scaling factor = 6283185.307

The first element in the lowpass prototype is a shunt inductor and for the lowpass to bandpass transformation the inductor is replaced by a capacitor and inductor in parallel with the same normalized value. The normalized values are then frequency scaled.

# C1 is 1st shunt capacitor
C1n = 1 # normalized value from the tables in the filter handbook
C1p = C1n/(FSF*Z)
print('capacitor = {:.3f}nF'.format(C1p*1e9))

wo = 2*np.pi*fo
L1p = 1/(wo**2*C1p) # calculate the value of the inductor that resonates with the capacitor at the center frequency
print('inductor = {:.3f}nH'.format(L1p*1e9))
capacitor = 3.183nF
inductor = 79.777nH

The second element in the lowpass prototype is a series capacitor and for the lowpass to bandpass transformation the capacitor is replaced by a capacitor and inductor in series with the same normalized value. The normalized values are then frequency scaled.

# L2 is the 2nd series inductor
L2n = 2 # normalized value from the tables in the filter handbook
L2p = L2n*Z/FSF
print('inductor = {:.3f}uH'.format(L2p*1e6))

C2p = 1/(wo**2*L2p) # calculate the value of the capacitor that resonates with the inductor at the center frequency
print('capacitor = {:.3f}pF'.format(C2p*1e12))
inductor = 15.915uH
capacitor = 15.955pF

The last element in the lowpass prototype is a shunt inductor and for the lowpass to bandpass transformation the inductor is replaced by a capacitor and inductor in parallel with the same normalized value. The normalized values are then frequency scaled. The values for this branch of the circuit are the same as for the first shunt element.

21.4 Find z11 and z12

To find \(z_{11}\) and \(z_{21}\), remove V2 from the net list by commenting out the V2 line in the net list. This will make \(I_2\) equal to zero. \(z_{11}\) is equal to \(\frac{V_1}{I_1}\text{ when }I_2=0\) and \(z_{21}\) is equal to \(\frac{V_2}{I_1}\text{ when }I_2=0\).

The net list below was run through the NMA code to generate the circuit equations. The component values in the netlist have all been set to one. Later, the actual component values will be used.

V1 1 0 1
*V2 2 0 1
L1 0 1 1
L2 1 3 1
L3 0 2 1
C1 1 0 1
C2 3 2 1
C3 2 0 1
net_list = '''
V1 1 0 1
*V2 2 0 1
L1 0 1 1
L2 1 3 1
L3 0 2 1
C1 1 0 1
C2 3 2 1
C3 2 0 1
'''

Call the symbolic modified nodal analysis function

report, network_df, i_unk_df, A, X, Z = SymMNA.smna(net_list)

The network equations for the circuit can be obtained from the A, X and Z values returned from the smna function. The A, X and Z are formuloated into equations and displayed below. Markdown is an IPython function and latex is a SymPy printing function.

# reform X and Z into Matrix type for printing
Xp = Matrix(X)
Zp = Matrix(Z)
temp = ''
for i in range(len(X)):
    temp += '${:s}$<br>'.format(latex(Eq((A*Xp)[i:i+1][0],Zp[i])))

Markdown(temp)

\(C_{1} s v_{1} - I_{L1} + I_{L2} + I_{V1} = 0\)
\(- C_{2} s v_{3} - I_{L3} + v_{2} \left(C_{2} s + C_{3} s\right) = 0\)
\(- C_{2} s v_{2} + C_{2} s v_{3} - I_{L2} = 0\)
\(v_{1} = V_{1}\)
\(- I_{L1} L_{1} s - v_{1} = 0\)
\(- I_{L2} L_{2} s + v_{1} - v_{3} = 0\)
\(- I_{L3} L_{3} s - v_{2} = 0\)

# Put matrices into SymPy 
X = Matrix(X)
Z = Matrix(Z)

NE_sym = Eq(A*X,Z)
NE_sym

\(\displaystyle \left[\begin{matrix}C_{1} s v_{1} - I_{L1} + I_{L2} + I_{V1}\\- C_{2} s v_{3} - I_{L3} + v_{2} \left(C_{2} s + C_{3} s\right)\\- C_{2} s v_{2} + C_{2} s v_{3} - I_{L2}\\v_{1}\\- I_{L1} L_{1} s - v_{1}\\- I_{L2} L_{2} s + v_{1} - v_{3}\\- I_{L3} L_{3} s - v_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\0\\V_{1}\\0\\0\\0\end{matrix}\right]\)

# turn the free symbols into SymPy variables
var(str(NE_sym.free_symbols).replace('{','').replace('}',''))

\(\displaystyle \left( I_{L3}, \ C_{2}, \ L_{1}, \ C_{3}, \ I_{L1}, \ v_{3}, \ L_{2}, \ V_{1}, \ I_{L2}, \ s, \ v_{2}, \ C_{1}, \ v_{1}, \ I_{V1}, \ L_{3}\right)\)

Symbolic solution

U_sym_p1 = solve(NE_sym,X)

Display the symbolic solution

temp = ''
for i in U_sym_p1.keys():
    temp += '${:s} = {:s}$<br>'.format(latex(i),latex(U_sym_p1[i]))

Markdown(temp)

\(v_{1} = V_{1}\)
\(v_{2} = \frac{C_{2} L_{3} V_{1} s^{2}}{C_{2} C_{3} L_{2} L_{3} s^{4} + C_{2} L_{2} s^{2} + C_{2} L_{3} s^{2} + C_{3} L_{3} s^{2} + 1}\)
\(v_{3} = \frac{C_{2} L_{3} V_{1} s^{2} + C_{3} L_{3} V_{1} s^{2} + V_{1}}{C_{2} C_{3} L_{2} L_{3} s^{4} + C_{2} L_{2} s^{2} + C_{2} L_{3} s^{2} + C_{3} L_{3} s^{2} + 1}\)
\(I_{V1} = \frac{- C_{1} C_{2} C_{3} L_{1} L_{2} L_{3} V_{1} s^{6} - C_{1} C_{2} L_{1} L_{2} V_{1} s^{4} - C_{1} C_{2} L_{1} L_{3} V_{1} s^{4} - C_{1} C_{3} L_{1} L_{3} V_{1} s^{4} - C_{1} L_{1} V_{1} s^{2} - C_{2} C_{3} L_{1} L_{3} V_{1} s^{4} - C_{2} C_{3} L_{2} L_{3} V_{1} s^{4} - C_{2} L_{1} V_{1} s^{2} - C_{2} L_{2} V_{1} s^{2} - C_{2} L_{3} V_{1} s^{2} - C_{3} L_{3} V_{1} s^{2} - V_{1}}{C_{2} C_{3} L_{1} L_{2} L_{3} s^{5} + C_{2} L_{1} L_{2} s^{3} + C_{2} L_{1} L_{3} s^{3} + C_{3} L_{1} L_{3} s^{3} + L_{1} s}\)
\(I_{L1} = - \frac{V_{1}}{L_{1} s}\)
\(I_{L2} = \frac{C_{2} C_{3} L_{3} V_{1} s^{3} + C_{2} V_{1} s}{C_{2} C_{3} L_{2} L_{3} s^{4} + C_{2} L_{2} s^{2} + C_{2} L_{3} s^{2} + C_{3} L_{3} s^{2} + 1}\)
\(I_{L3} = - \frac{C_{2} V_{1} s}{C_{2} C_{3} L_{2} L_{3} s^{4} + C_{2} L_{2} s^{2} + C_{2} L_{3} s^{2} + C_{3} L_{3} s^{2} + 1}\)

element_values = {L1:L1p,C1:C1p,C2:C2p,L2:L2p,L3:L1p,C3:C1p}
U_p1 = solve(NE_sym.subs(element_values),X)

Substituting actual values for the circuit elements, we get:

U_p1

\(\displaystyle \left\{ I_{L1} : - \frac{12534954.6878233 V_{1}}{s}, \ I_{L2} : \frac{6.33074196165529 \cdot 10^{60} V_{1} s^{3} + 2.49302855720388 \cdot 10^{76} V_{1} s}{1.00756887663672 \cdot 10^{56} s^{4} + 7.95544497540355 \cdot 10^{71} s^{2} + 1.5625 \cdot 10^{87}}, \ I_{L3} : - \frac{2.49302855720388 \cdot 10^{76} V_{1} s}{1.00756887663672 \cdot 10^{56} s^{4} + 7.95544497540355 \cdot 10^{71} s^{2} + 1.5625 \cdot 10^{87}}, \ I_{V1} : \frac{- 6.39649568809606 \cdot 10^{83} V_{1} s^{6} - 7.58201892757332 \cdot 10^{99} V_{1} s^{4} - 2.98577794233115 \cdot 10^{115} V_{1} s^{2} - 3.90625 \cdot 10^{130} V_{1}}{2.00951838624414 \cdot 10^{92} s^{5} + 1.58665212071561 \cdot 10^{108} s^{3} + 3.11628569650484 \cdot 10^{123} s}, \ v_{1} : V_{1}, \ v_{2} : \frac{1.98886124385089 \cdot 10^{69} V_{1} s^{2}}{1.00756887663672 \cdot 10^{56} s^{4} + 7.95544497540355 \cdot 10^{71} s^{2} + 1.5625 \cdot 10^{87}}, \ v_{3} : \frac{3.98766679392103 \cdot 10^{71} V_{1} s^{2} + 1.5625 \cdot 10^{87} V_{1}}{1.00756887663672 \cdot 10^{56} s^{4} + 7.95544497540355 \cdot 10^{71} s^{2} + 1.5625 \cdot 10^{87}}\right\}\)

\(z_{11} = \frac {V_1}{I_1} \text{ when } I_2=0\)

z11 = (-U_p1[v1]/U_p1[I_V1]).cancel()
z11 #.simplify()

\(\displaystyle \frac{2.00951838624414 \cdot 10^{92} s^{5} + 1.58665212071561 \cdot 10^{108} s^{3} + 3.11628569650484 \cdot 10^{123} s}{6.39649568809606 \cdot 10^{83} s^{6} + 7.58201892757332 \cdot 10^{99} s^{4} + 2.98577794233115 \cdot 10^{115} s^{2} + 3.90625 \cdot 10^{130}}\)

\(z_{21} = \frac {V_2}{I_1} \text{ when } I_2=0\)

z21 = (-U_p1[v2]/U_p1[I_V1]).cancel()
z21 #.simplify()

\(\displaystyle \frac{3.99665323720675 \cdot 10^{161} s^{7} + 3.1556309103651 \cdot 10^{177} s^{5} + 6.19785984654536 \cdot 10^{192} s^{3}}{6.44490997486657 \cdot 10^{139} s^{10} + 1.27281032416988 \cdot 10^{156} s^{8} + 1.00396628165841 \cdot 10^{172} s^{6} + 3.95359126276846 \cdot 10^{187} s^{4} + 7.77287372840943 \cdot 10^{202} s^{2} + 6.103515625 \cdot 10^{217}}\)

21.5 Find z12 & z22

To find \(z_{12}\) and \(z_{22}\), remove V1 from the net list by commenting out the V1 line in the netlist. This will make \(I_1\) equal to zero. \(z_{12}\) is equal to \(\frac{V_1}{I_2}\text{ when }I_1=0\) and \(z_{22}\) is equal to \(\frac{V_2}{I_2}\text{ when }I_1=0\).

The net list below was run through the NMA code to generate the circuit equations.

Remove V1 from the netlist.

net_list = '''
*V1 1 0 1
V2 2 0 1
L1 0 1 1
L2 1 3 1
L3 0 2 1
C1 1 0 1
C2 3 2 1
C3 2 0 1
'''

Call the symbolic modified nodal analysis function

report, network_df, i_unk_df, A, X, Z = SymMNA.smna(net_list)

The network equations for the circuit can be obtained from the A, X and Z values returned from the smna function. The A, X and Z are formuloated into equations and displayed below. Markdown is an IPython function and latex is a SymPy printing function.

# reform X and Z into Matrix type for printing
Xp = Matrix(X)
Zp = Matrix(Z)
temp = ''
for i in range(len(X)):
    temp += '${:s}$<br>'.format(latex(Eq((A*Xp)[i:i+1][0],Zp[i])))

Markdown(temp)

\(C_{1} s v_{1} - I_{L1} + I_{L2} = 0\)
\(- C_{2} s v_{3} - I_{L3} + I_{V2} + v_{2} \left(C_{2} s + C_{3} s\right) = 0\)
\(- C_{2} s v_{2} + C_{2} s v_{3} - I_{L2} = 0\)
\(v_{2} = V_{2}\)
\(- I_{L1} L_{1} s - v_{1} = 0\)
\(- I_{L2} L_{2} s + v_{1} - v_{3} = 0\)
\(- I_{L3} L_{3} s - v_{2} = 0\)

# Put matrices into SymPy 
X = Matrix(X)
Z = Matrix(Z)

NE_sym = Eq(A*X,Z)
NE_sym

\(\displaystyle \left[\begin{matrix}C_{1} s v_{1} - I_{L1} + I_{L2}\\- C_{2} s v_{3} - I_{L3} + I_{V2} + v_{2} \left(C_{2} s + C_{3} s\right)\\- C_{2} s v_{2} + C_{2} s v_{3} - I_{L2}\\v_{2}\\- I_{L1} L_{1} s - v_{1}\\- I_{L2} L_{2} s + v_{1} - v_{3}\\- I_{L3} L_{3} s - v_{2}\end{matrix}\right] = \left[\begin{matrix}0\\0\\0\\V_{2}\\0\\0\\0\end{matrix}\right]\)

# turn the free symbols into SymPy variables
var(str(NE_sym.free_symbols).replace('{','').replace('}',''))

\(\displaystyle \left( I_{L3}, \ C_{2}, \ L_{1}, \ C_{3}, \ I_{V2}, \ I_{L1}, \ v_{3}, \ L_{2}, \ I_{L2}, \ s, \ v_{2}, \ C_{1}, \ v_{1}, \ V_{2}, \ L_{3}\right)\)

Symbolic solution

U_sym_p2 = solve(NE_sym,X)

Display the symbolic solution

temp = ''
for i in U_sym_p2.keys():
    temp += '${:s} = {:s}$<br>'.format(latex(i),latex(U_sym_p2[i]))

Markdown(temp)

\(v_{1} = \frac{C_{2} L_{1} V_{2} s^{2}}{C_{1} C_{2} L_{1} L_{2} s^{4} + C_{1} L_{1} s^{2} + C_{2} L_{1} s^{2} + C_{2} L_{2} s^{2} + 1}\)
\(v_{2} = V_{2}\)
\(v_{3} = \frac{C_{1} C_{2} L_{1} L_{2} V_{2} s^{4} + C_{2} L_{1} V_{2} s^{2} + C_{2} L_{2} V_{2} s^{2}}{C_{1} C_{2} L_{1} L_{2} s^{4} + C_{1} L_{1} s^{2} + C_{2} L_{1} s^{2} + C_{2} L_{2} s^{2} + 1}\)
\(I_{V2} = \frac{- C_{1} C_{2} C_{3} L_{1} L_{2} L_{3} V_{2} s^{6} - C_{1} C_{2} L_{1} L_{2} V_{2} s^{4} - C_{1} C_{2} L_{1} L_{3} V_{2} s^{4} - C_{1} C_{3} L_{1} L_{3} V_{2} s^{4} - C_{1} L_{1} V_{2} s^{2} - C_{2} C_{3} L_{1} L_{3} V_{2} s^{4} - C_{2} C_{3} L_{2} L_{3} V_{2} s^{4} - C_{2} L_{1} V_{2} s^{2} - C_{2} L_{2} V_{2} s^{2} - C_{2} L_{3} V_{2} s^{2} - C_{3} L_{3} V_{2} s^{2} - V_{2}}{C_{1} C_{2} L_{1} L_{2} L_{3} s^{5} + C_{1} L_{1} L_{3} s^{3} + C_{2} L_{1} L_{3} s^{3} + C_{2} L_{2} L_{3} s^{3} + L_{3} s}\)
\(I_{L1} = - \frac{C_{2} V_{2} s}{C_{1} C_{2} L_{1} L_{2} s^{4} + C_{1} L_{1} s^{2} + C_{2} L_{1} s^{2} + C_{2} L_{2} s^{2} + 1}\)
\(I_{L2} = \frac{- C_{1} C_{2} L_{1} V_{2} s^{3} - C_{2} V_{2} s}{C_{1} C_{2} L_{1} L_{2} s^{4} + C_{1} L_{1} s^{2} + C_{2} L_{1} s^{2} + C_{2} L_{2} s^{2} + 1}\)
\(I_{L3} = - \frac{V_{2}}{L_{3} s}\)

U_p2 = solve(NE_sym.subs(element_values),X)
U_p2

\(\displaystyle \left\{ I_{L1} : - \frac{9.9721142288155 \cdot 10^{74} V_{2} s}{4.03027550654688 \cdot 10^{54} s^{4} + 3.18217799016142 \cdot 10^{70} s^{2} + 6.25 \cdot 10^{85}}, \ I_{L2} : \frac{- 2.53229678466212 \cdot 10^{59} V_{2} s^{3} - 9.9721142288155 \cdot 10^{74} V_{2} s}{4.03027550654688 \cdot 10^{54} s^{4} + 3.18217799016142 \cdot 10^{70} s^{2} + 6.25 \cdot 10^{85}}, \ I_{L3} : - \frac{12534954.6878233 V_{2}}{s}, \ I_{V2} : \frac{- 6.39649568809606 \cdot 10^{83} V_{2} s^{6} - 7.58201892757332 \cdot 10^{99} V_{2} s^{4} - 2.98577794233115 \cdot 10^{115} V_{2} s^{2} - 3.90625 \cdot 10^{130} V_{2}}{2.00951838624414 \cdot 10^{92} s^{5} + 1.58665212071561 \cdot 10^{108} s^{3} + 3.11628569650484 \cdot 10^{123} s}, \ v_{1} : \frac{7.95544497540356 \cdot 10^{67} V_{2} s^{2}}{4.03027550654688 \cdot 10^{54} s^{4} + 3.18217799016142 \cdot 10^{70} s^{2} + 6.25 \cdot 10^{85}}, \ v_{2} : V_{2}, \ v_{3} : \frac{4.03027550654688 \cdot 10^{54} V_{2} s^{4} + 1.59506671756841 \cdot 10^{70} V_{2} s^{2}}{4.03027550654688 \cdot 10^{54} s^{4} + 3.18217799016142 \cdot 10^{70} s^{2} + 6.25 \cdot 10^{85}}\right\}\)

\(z_{12} = \frac {V_1}{I_2} \text{ when } I_1=0\)

z12 = (-U_p2[v1]/U_p2[I_V2]).cancel()
z12 #.simplify()

\(\displaystyle \frac{1.5986612948827 \cdot 10^{160} s^{7} + 1.26225236414604 \cdot 10^{176} s^{5} + 2.47914393861815 \cdot 10^{191} s^{3}}{2.57796398994663 \cdot 10^{138} s^{10} + 5.09124129667954 \cdot 10^{154} s^{8} + 4.01586512663364 \cdot 10^{170} s^{6} + 1.58143650510738 \cdot 10^{186} s^{4} + 3.10914949136377 \cdot 10^{201} s^{2} + 2.44140625 \cdot 10^{216}}\)

\(z_{22} = \frac {V_2}{I_2} \text{ when } I_1=0\)

z22 = (-U_p2[v2]/U_p2[I_V2]).cancel()
z22 #.simplify()

\(\displaystyle \frac{2.00951838624414 \cdot 10^{92} s^{5} + 1.58665212071561 \cdot 10^{108} s^{3} + 3.11628569650484 \cdot 10^{123} s}{6.39649568809606 \cdot 10^{83} s^{6} + 7.58201892757332 \cdot 10^{99} s^{4} + 2.98577794233115 \cdot 10^{115} s^{2} + 3.90625 \cdot 10^{130}}\)

displaying the z-parameter matrix

Matrix([[z11,z21],[z12,z22]])

\(\displaystyle \left[\begin{matrix}\frac{2.00951838624414 \cdot 10^{92} s^{5} + 1.58665212071561 \cdot 10^{108} s^{3} + 3.11628569650484 \cdot 10^{123} s}{6.39649568809606 \cdot 10^{83} s^{6} + 7.58201892757332 \cdot 10^{99} s^{4} + 2.98577794233115 \cdot 10^{115} s^{2} + 3.90625 \cdot 10^{130}} & \frac{3.99665323720675 \cdot 10^{161} s^{7} + 3.1556309103651 \cdot 10^{177} s^{5} + 6.19785984654536 \cdot 10^{192} s^{3}}{6.44490997486657 \cdot 10^{139} s^{10} + 1.27281032416988 \cdot 10^{156} s^{8} + 1.00396628165841 \cdot 10^{172} s^{6} + 3.95359126276846 \cdot 10^{187} s^{4} + 7.77287372840943 \cdot 10^{202} s^{2} + 6.103515625 \cdot 10^{217}}\\\frac{1.5986612948827 \cdot 10^{160} s^{7} + 1.26225236414604 \cdot 10^{176} s^{5} + 2.47914393861815 \cdot 10^{191} s^{3}}{2.57796398994663 \cdot 10^{138} s^{10} + 5.09124129667954 \cdot 10^{154} s^{8} + 4.01586512663364 \cdot 10^{170} s^{6} + 1.58143650510738 \cdot 10^{186} s^{4} + 3.10914949136377 \cdot 10^{201} s^{2} + 2.44140625 \cdot 10^{216}} & \frac{2.00951838624414 \cdot 10^{92} s^{5} + 1.58665212071561 \cdot 10^{108} s^{3} + 3.11628569650484 \cdot 10^{123} s}{6.39649568809606 \cdot 10^{83} s^{6} + 7.58201892757332 \cdot 10^{99} s^{4} + 2.98577794233115 \cdot 10^{115} s^{2} + 3.90625 \cdot 10^{130}}\end{matrix}\right]\)

The complete z-parameter matrix is shown above. It’s kind of interesting that the exponents on many of the coefficients are so large. Using the SymPy function lambdify to turn the symbolic expression into a function.

func_z11_s = lambdify(s, z11)
func_z12_s = lambdify(s, z12) 
func_z21_s = lambdify(s, z21) 
func_z22_s = lambdify(s, z22) 

Using the results from above, the input impedance of a two-port network is given by: The input impedance of a two-port network is: \(Z_{in}=\frac{Z_{11}Z_{22}+Z_{11}Z_{L}-Z_{12}Z_{21}}{Z_{22}+Z_{L}}\), where \(Z_{L}\) is the impedance of the load connected to port two.

# set the source and load impedance
Zl = Zs = 50

w = np.linspace(9e6*2*np.pi, 11e6*2*np.pi, 1000, endpoint=True)

plt.title('Input impedance')

plt.plot(w/(2*np.pi)/1e6, np.abs((func_z11_s(1j*w)*func_z22_s(1j*w)+func_z11_s(1j*w)*Zl-func_z12_s(1j*w)*func_z21_s(1j*w))/(func_z22_s(1j*w)+Zl)),'-',label='|Zin|')
plt.plot(w/(2*np.pi)/1e6, np.real((func_z11_s(1j*w)*func_z22_s(1j*w)+func_z11_s(1j*w)*Zl-func_z12_s(1j*w)*func_z21_s(1j*w))/(func_z22_s(1j*w)+Zl)),'-',label='Re Zin')
plt.plot(w/(2*np.pi)/1e6, np.imag((func_z11_s(1j*w)*func_z22_s(1j*w)+func_z11_s(1j*w)*Zl-func_z12_s(1j*w)*func_z21_s(1j*w))/(func_z22_s(1j*w)+Zl)),'-',label='Im Zin')

plt.ylabel('impedance, ohms')
plt.xlabel('Frequency, MHz')
plt.legend()
plt.grid()
plt.show()

The plot above shows the magnitude of the input impedance (in blue), the real part of the input impedance (in orange) and the imaginary part of the input impedance (in green). Near 10Hz, the magnitude and real part of the input impedance is 50 ohms and the imaginary part is about zero. This shows that the design equations produced the desired results. The input impedance bandwidth of the filter is about 1 MHz.

The voltage gain of the filter calculated from the z-parameters is: \(K_v=\frac{Z_{21}Z_{L}}{Z_{11}Z_{22}+Z_{11}Z_{L}-Z_{12}Z_{21}}\), which is plotted below:

plt.title('Voltage gain')
plt.plot(w/(2*np.pi)/1e6, np.abs((func_z21_s(1j*w)*Zl)/(func_z11_s(1j*w)*func_z22_s(1j*w)+func_z11_s(1j*w)*Zl-func_z12_s(1j*w)*func_z21_s(1j*w))),'-',label='|Kv|')
plt.ylabel('Kv')
plt.xlabel('Frequency, MHz')
plt.legend()
plt.grid()
plt.show()

The plot above shows the voltage gain of the filter (in blue) plotted from the z-parameters. The filter is centered at 10MHz and the bandwidth appears to be 1 MHz.

21.6 Convert z-parameters to s-parameters

Although applicable at any frequency, s-parameters are mostly used for networks operating at radio frequency (RF) and microwave frequencies. The s-parameters can be calculated from the z-parameters with the following formulas:

Z_o = 50 # characteristic impedance

w = np.linspace(8e6*2*np.pi, 12e6*2*np.pi, 2000, endpoint=True)
s11 = np.zeros(len(w),dtype=complex)
s12 = np.zeros(len(w),dtype=complex)
s21 = np.zeros(len(w),dtype=complex)
s22 = np.zeros(len(w),dtype=complex)

for i in range(len(w)):
    del_z = (func_z11_s(1j*w[i])+Z_o)*(func_z22_s(1j*w[i])+Z_o)-func_z12_s(1j*w[i])*func_z21_s(1j*w[i])
    s11[i] = ((func_z11_s(1j*w[i])-Z_o)*(func_z22_s(1j*w[i])+Z_o)-func_z12_s(1j*w[i])*func_z21_s(1j*w[i]))/del_z
    s12[i] = (2*func_z12_s(1j*w[i])*Z_o)/del_z
    s21[i] = (2*func_z21_s(1j*w[i])*Z_o)/del_z
    s22[i] = ((func_z11_s(1j*w[i])+Z_o)*(func_z22_s(1j*w[i])-Z_o)-func_z12_s(1j*w[i])*func_z21_s(1j*w[i]))/del_z

plt.title('S-parameters')
plt.plot(w/(2*np.pi)/1e6, 20*np.log10(np.abs(s11)),'-',label='|s11|dB')
plt.plot(w/(2*np.pi)/1e6, 20*np.log10(np.abs(s21)),'-',label='|s21|dB')
plt.ylabel('|S11| & |S21|, dB')
plt.xlabel('Frequency, MHz')
plt.ylim((-20,1))
plt.yticks(np.arange(-20, 1, 5))
#plt.xlim((-1,20))
#plt.xticks(np.arange(0, 20+1, 2.0))
plt.legend()
plt.grid()
plt.show()

The plot above shows the magnitudes in dB of the input reflection coefficient (in blue) and the forward voltage gain (in orange).

The magnitude of the input reflection coefficient indicates the amount of energy reflected back to the source due to the impedance mismatch. The smaller the reflection coefficient the better the match, and in dB, large negative values are very small small numbers. A rule of thumb is that |S11| less than about -9.5 dB is a reasonable match, since this corresponds to about a 2:1 VSWR or a mismatch loss of about 0.5 dB. The frequency range over which this match occurs is called the impedance bandwidth.

The plot of |S21| shows the forward voltage gain of the filter in dB. The filter was designed to be a band pass filter with a center frequency of 10 MHz, which is clearly shown in the plot. The plot is similar to what would be seen on a network analyzer.

21.7 Summary

In this notebook the parameters for two port networks were generated from the circuit’s netlist. Using python, the circuit equations were automatically generated by using modified nodal analysis, then SymPy was used to solve for the network currents and voltages. Z-parameters were calculated for the circuits, from which y and s parameters were generated. The z-parameters were used to generate input impedance and transfer functions.

This notebook has demonstrated that two port parameters can easily be generated from a circuit’s netlist with python.